Session 3: Deep Learning and Training
- [ ] Reducing Energy Bloat in Large Model Training
在众多GPU上训练大型AI模型消耗大量能量,使得电力供应成为构建和运营AI工作负载数据中心的最大限制因素之一。然而,并非所有在训练过程中消耗的能量都会提高训练过程的吞吐量,本文称浪费的能量为“能量膨胀”(energy bloat)。
尽管最近有一些加速大型模型训练的研究,但降低功耗仍然是一个未被彻底解决的问题,其在硬件领域得到了充分研究,但单靠硬件进步所带来的能效提升不足以满足日益增长的AI计算需求。
软件在功耗优化中可以发挥重要作用,因为它能够捕获通用硬件无法获取的应用特性,从而带来与硬件无关的能效提升。
- [ ] Uncovering Nested Data Parallelism and Data Reuse in DNN Computation with FractalTensor
深度神经网络(DNN)的计算过程通常被抽象为一系列张量算子(Tensor Operator)组成的有向无环图(DAG)。这些算子会被实现为加速器函数,例如GPU kernel。因此,DNN性能提高通常依赖于对算子进行深度的优化。
虽然DAG这种抽象很符合直觉,但它也带来了一些问题。首先,DAG的表达能力弱,难以支持很多新的DNN算法。其次,由于DAG表达能力的局限性,用户要么使用更灵活的命令式编程接口(如PyTorch)来实现新的DNN算法,牺牲效率;要么不断引入新的张量算子,并依赖专家的经验来手工地优化,这不仅需要大量的工作,也使得端到端程序分析和优化变得复杂。
作者认为,DAG的根本问题在于其无法表达在张量(也就是高维数组)元素上迭代的嵌套循环。嵌套循环在DNN计算中非常常见,并且循环间会存在复杂的潜在并行性以及数据依赖关系,这些语义在DAG的抽象中已经完全丢失,导致后续的程序分析以及编译优化会非常困难。
- [ ] Enabling Parallelism Hot Switching for Efficient Training of Large Language Models
这篇论文提出了 HotSpa,一个训练系统,使得大模型训练任务能够感知输入序列长度并动态调整并行策略。从而减少不必要的通信开销,充分利用硬件内存和计算资源。
随着大型语言模型(LLM)的发展,训练这些模型变得越来越复杂和资源密集。为了处理大规模的模型和数据,通常需要跨多个设备进行并行化。并行化策略在处理内存消耗、计算成本和通信成本方面起着关键作用。然而,目前的系统通常假设训练任务中的工作负载是均匀的,并在整个训练过程中使用静态的并行化策略。
这种假设在处理序列输入时并不成立,因为序列的长度在不同样本间差异很大。具体来说,训练过程中从数据集中抽取的小批次序列,其长度往往分布不均匀:大部分序列较短,而少数序列较长。这种长度差异导致了不同样本之间的工作负载不平衡。
- [ ] Tenplex: Dynamic Parallelism for Deep Learning using Parallelizable Tensor Collections
这篇论文提出了Tenplex,一个状态管理库,使深度学习任务能够在运行时动态改变并行度。Tenplex通过引入可并行张量集合(PTC)来实现这一点,允许在GPU变更后高效地转换任务状态。
- [x] ReCycle: Resilient Training of Large DNNs using Pipeline Adaptation
随着深度学习模型参数规模的不断增大,训练模型所需要的GPU数量也越来越多,导致GPU集群中出现故障的频率显著增高。根据微软的一个大规模训练集群的数据,平均每45分钟就会出现一次机器故障。Meta在训练OPT 175B模型时也因机器故障浪费了17.8万GPU小时。
在训练中出现故障一般有两种处理方法:
- 准备一定数量的备用机器,用于替换出现故障的机器。这种方法因为存在空闲的资源,因此训练成本会增加。例如在训练GPT 530B时,需要为一整个DP group准备备用机器(280张GPU),大约占整个集群的11%。
- 不使用备用机器,而是直接去除出现的故障的DP group的机器。这样会导致训练的吞吐降低。
相关的工作如Oobleck和Bamboo利用流水线并行的特点,可以在不使用备用机器的情况下提高出现故障时训练的吞吐。Oobleck通过提前制定不同资源配置下的流水线方案,在存在机器出现故障时切换到用更少的节点的流水线方案上,从而减少故障带来的资源闲置。
本文采取了与Oobleck截然不同的思路:流水线并行中存在气泡,在一个流水线阶段的机器发生故障后,可以尝试将该阶段的计算任务调度到其他机器的气泡内,从而能够隐藏故障带来的开销。
Session 9: ML Serving
- [x] PowerInfer: Fast Large Language Model Serving with a Consumer-grade GPU
在消费级GPU上部署LLMs面临着巨大挑战,这主要是由于其庞大的显存需求。当模型参数对显存的需求超出GPU显存时,现有的参数卸载方法都会引入显著的速度下降问题。本文认为LLM推理中内存问题的关键原因是硬件架构与LLM推理特性之间的局部性不匹配。当前的硬件架构设计了一个针对数据局部性优化的内存层次结构。理想情况下,一个小的、频繁访问的工作集应该存储在GPU中。相比之下,更大的、访问频率较低的数据更适合存储在CPU中。然而,每次LLM推理迭代都需要访问整个参数集,其总大小太大而无法装入单个GPU,因此没有表现出任何局部性,从而阻碍了高效的局部性利用。
LLM中的神经元激活遵循幂律分布:一小部分神经元在各种输入中持续贡献了大部分激活(超过80%)(热激活),而大部分神经元参与剩余的激活,这些激活是在运行时根据输入确定的(冷激活)。这一观察表明LLMs在高激活稀疏性中存在固有的激活局部性,可以用来解决前面提到的局部性不匹配问题。
- [x] Apparate: Rethinking Early Exits to Tame Latency-Throughput Tensions in ML Serving
Apparate是一个针对早退机制(Early Exits)设计的ML推理系统。传统工作通常通过调节batch size来进行时延和吞吐之间的trade-off,往往为了达到时延SLO而被迫降低系统并发度,导致计算资源利用率低。经典的早退机制利用模型推理的某个中间结果,预先完成结果预测。当预测的确信度高于阈值时可以直接返回预测结果,跳过后续计算,从而减小推理时延。Apparate能够自动化地为模型添加早退机制,并在推理过程中利用监视器监控推理性能和准确度,从而指导动态调整早退判定插入点以及确信度阈值。Apparate在保障推理精度和吞吐的同时分别实现了40.5–91.5%(CV)、10.0–24.2%(NLP)和22.6–77.9%(LLM)的P50时延下降。
- [ ] Improving DNN Inference Throughput Using Practical, Per-Input Compute Adaptation
随着机器学习推理平台面临请求速率的增长和严格的延迟限制,现有的解决方案主要通过压缩模型来降低计算时延,同时会轻微地牺牲准确率,例如知识蒸馏 (distillation),剪枝 (pruning),以及量化 (quantization)。本文探索了另一种模型加速技术,即早期退出模型 (early-exit networks, EE-DNNs),它允许某些输入从模型的中间层退出,从而在每个输入的粒度上平衡准确性和资源开销。
- [x] LoongServe: Efficiently Serving Long-Context Large Language Models with Elastic Sequence Parallelism
大语言模型(LLM)支持的上下文窗口越来越长,例如 Anthropic 的 Claude-3、Google 的 Gemini-1.5 和 UCB 的 Large Worlkd Model (LWM),都支持 1M 的上下文窗口。
为长上下文 LLM 提供服务在 GPU 计算和 GPU 内存方面向 LLM 服务系统提出了重大挑战。在服务过程中,键值缓存的 GPU 内存消耗随着序列长度线性增长。当 LWM 模型处理输入长度为 1M 令牌的单个请求时,仅键值缓存就可以达到 488GB,远远超过当今最先进 GPU 的 GPU 内存容量。至于计算需求,长上下文LLM(例如LWM)中注意力机制的复杂性与输入序列长度成二次方,使得长序列的处理计算量更大。
为了加速 LLM 计算,许多工作利用了不同的并行维度。Model Parallelism 将模型参数划分到多个 GPU 上以并行化计算。Sequence Parallelism 跨 GPU 划分请求的输入序列以实现加速。无论使用其中之一还是两者的组合,现有实践都必须在启动服务之前静态地定义并行配置。
然而,LLM 推理工作负载是高度动态的。随着 LLM 的上下文窗口的增加,请求的输入长度的方差变得更大,导致不同请求的计算需求不同。此外,请求处理分为两个阶段,Prefill 阶段和 Decode 阶段。同一请求在不同阶段的资源需求也有很大差异。因此,现有的静态并行性对于不同输入长度的请求以及同一请求的不同阶段都效率不高。
一种可能的解决方案是将 GPU 组织成多个组。每个组部署一个 LLM 实例,并采用不同的并行策略来处理特定序列长度范围或特定阶段的序列。然而,它们的静态分组策略常常与不同阶段不同请求的资源需求不匹配,因为资源需求在 Iteration 的粒度上动态变化。此外,这些解决方案在过渡阶段时会迁移所有请求的键值缓存,从而产生额外的通信开销。由于组之间的隔离,不同组之间的 GPU 内存无法一起用于服务长序列长度的请求,从而导致 GPU 内存碎片。